#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "taskdialog.h"
#include "serialization.h"
#include "helpers.h"
#include "workflow.h"
#include "notification.h"
#include <QtCore/QDir>
#include <QtCore/QTimer>
#include <QtGui/QMessageBox>
#include <QtGui/QMenuBar>
#include <QtGui/QInputDialog>

namespace acidone
{

MainWindow::MainWindow(QWidget *parent)
	:QMainWindow(parent)
	,ui(new Ui::MainWindow)
	,m_database(0)
	,m_databaseDirty(false)
	,m_categoryManager(0)
	,m_workflow(0)
	,m_notificationManager(0)
	,m_meSubject(0)
{
    ui->setupUi(this);
	createActions();
	createMenus();

	ui->addTaskButton->setDefaultAction(m_newTaskAct);
	ui->editTaskButton->setDefaultAction(m_editTaskAct);
	ui->doneTaskButton->setDefaultAction(m_doneTaskAct);

	connect(ui->categorySelector, SIGNAL(currentIndexChanged(int)), this, SLOT(categoryChanged(int)));
	connect(ui->taskList, SIGNAL(itemActivated(QListWidgetItem*)), m_editTaskAct, SLOT(trigger()));
	connect(ui->taskList, SIGNAL(currentRowChanged(int)), this, SLOT(updateActions()));

	if (!loadDatabase())
	{
		// create empty database
		m_database = new Database(this);
	}

	setUpDatabase();
	reloadCategories();

	m_workflow = new AcidWorkflow(m_categoryManager);
	m_notificationManager = new NotificationManager(m_database, this);

	connect(m_notificationManager, SIGNAL(tasksStarted(Database::TaskList)), this, SLOT(tasksStarted(Database::TaskList)));

	QTimer* saveTimer = new QTimer(this);
	connect(saveTimer, SIGNAL(timeout()), this, SLOT(saveDatabase()));
	saveTimer->start(60 * 1000); // every minute

	setWindowTitle(tr("Acidone"));
}

MainWindow::~MainWindow()
{
	saveDatabase();
	delete m_workflow;
	delete m_categoryManager;
	delete ui;
}

void MainWindow::createActions()
{
	m_newTaskAct = new QAction(tr("New"), this);
	connect(m_newTaskAct, SIGNAL(triggered()), this, SLOT(newTask()));

	m_editTaskAct = new QAction(tr("Edit"), this);
	connect(m_editTaskAct, SIGNAL(triggered()), this, SLOT(editCurrentTask()));

	m_doneTaskAct = new QAction(tr("Done"), this);
	connect(m_doneTaskAct, SIGNAL(triggered()), this, SLOT(doneCurrentTask()));

	m_removeTaskAct = new QAction(tr("Delete"), this);
	connect(m_removeTaskAct, SIGNAL(triggered()), this, SLOT(removeCurrentTask()));

	m_exitAct = new QAction(tr("Exit"), this);
	connect(m_exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));

	m_newCategoryAct = new QAction(tr("New"), this);
	connect(m_newCategoryAct, SIGNAL(triggered()), this, SLOT(newCategory()));

	m_removeCategoryAct = new QAction(tr("Delete"), this);
	connect(m_removeCategoryAct, SIGNAL(triggered()), this, SLOT(removeCurrentCategory()));

	m_renameCategoryAct = new QAction(tr("Rename"), this);
	connect(m_renameCategoryAct, SIGNAL(triggered()), this, SLOT(renameCurrentCategory()));

	m_newSubjectAct = new QAction(tr("New"), this);
	connect(m_newSubjectAct, SIGNAL(triggered()), this, SLOT(newSubject()));

	m_manageSubjectsAct = new QAction(tr("Manage"), this);
	connect(m_manageSubjectsAct, SIGNAL(triggered()), this, SLOT(manageSubjects()));

	m_showSettingsAct = new QAction(tr("Settings"), this);
	connect(m_showSettingsAct, SIGNAL(triggered()), this, SLOT(showSettings()));
}

void MainWindow::createMenus()
{
	QMenu* taskMenu = new QMenu(tr("Task"));

	taskMenu->addAction(m_newTaskAct);
	taskMenu->addAction(m_editTaskAct);
	taskMenu->addAction(m_removeTaskAct);
	taskMenu->addAction(m_doneTaskAct);

	menuBar()->addMenu(taskMenu);

	QMenu* categoryMenu = new QMenu(tr("Categories"));
	categoryMenu->addAction(m_newCategoryAct);
	categoryMenu->addAction(m_removeCategoryAct);
	categoryMenu->addAction(m_renameCategoryAct);

	menuBar()->addMenu(categoryMenu);

	QMenu* subjectMenu = new QMenu(tr("Subjects"));
	subjectMenu->addAction(m_newSubjectAct);
	subjectMenu->addAction(m_manageSubjectsAct);

	menuBar()->addMenu(subjectMenu);

	menuBar()->addAction(m_showSettingsAct);
	menuBar()->addAction(m_exitAct);

	QAction* myMenuKey = new QAction(tr("Hide"), this);
	myMenuKey->setSoftKeyRole(QAction::NegativeSoftKey);
	connect(myMenuKey, SIGNAL(triggered()), this, SLOT(lower()));
	addAction(myMenuKey);
}

bool MainWindow::loadDatabase()
{
	Q_ASSERT(m_database == 0);

	QFile dbFile(databasePath());
	if (dbFile.open(QFile::ReadOnly))
	{
		m_database = acidone::loadDatabase(&dbFile);
		if (m_database)
		{
			m_database->setParent(this);
			return true;
		}
	}
	return false;
}

void MainWindow::setUpDatabase()
{
	Q_ASSERT(m_database != 0);
	Q_ASSERT(m_categoryManager == 0);

	connect(m_database, SIGNAL(changed()), this, SLOT(databaseChanged()));

	m_meSubject = 0;

	m_categoryManager = new CategoryManager(m_database);

	m_categoryManager->setEmbeddedCategory(CategoryManager::In, addOrFindCategory("***IN"));
	m_categoryManager->setEmbeddedCategory(CategoryManager::Day, addOrFindCategory("**DAY"));
	m_categoryManager->setEmbeddedCategory(CategoryManager::Later, addOrFindCategory("**LATER"));
	m_categoryManager->setEmbeddedCategory(CategoryManager::Control, addOrFindCategory("*CONTROL"));
	m_categoryManager->setEmbeddedCategory(CategoryManager::Maybe, addOrFindCategory(">>MAYBE"));
	m_categoryManager->setEmbeddedCategory(CategoryManager::Done, addOrFindCategory("DONE"));

	m_meSubject = addOrFindSubject("Me");
}

void MainWindow::reloadCategories()
{
	const QString selectedCategory = ui->categorySelector->currentText();

	ui->categorySelector->clear();
	Database::Categories categories = m_database->categories();

	foreach(Category* category, categories)
	{
		ui->categorySelector->addItem(category->name(), QVariant::fromValue(category));
		if (category->name() == selectedCategory)
			ui->categorySelector->setCurrentIndex(ui->categorySelector->count()-1);
	}
}

void MainWindow::reloadTasks()
{
	ui->taskList->clear();

	if (Category* category = currentCategory())
	{
		setTaskList(m_database->tasksByCategory(category));
	}
}

void MainWindow::updateActions()
{
	const bool hasSelectedTask = ui->taskList->currentItem() != 0;
	const bool taskCanBeDone = hasSelectedTask && m_workflow->canBeDone(currentTask());

	m_editTaskAct->setEnabled(hasSelectedTask);
	m_doneTaskAct->setEnabled(hasSelectedTask && taskCanBeDone);
	m_removeTaskAct->setEnabled(hasSelectedTask);

	const bool canManageCategory = !m_categoryManager->isEmbeddedCategory(currentCategory());
	m_renameCategoryAct->setEnabled(canManageCategory);
	m_removeCategoryAct->setEnabled(canManageCategory);
}

Task* MainWindow::currentTask() const
{
	if (ui->taskList->currentItem() != 0)
	{
		return ui->taskList->currentItem()->data(Qt::UserRole).value<Task*>();
	}
	else
	{
		return 0;
	}
}

Category* MainWindow::currentCategory() const
{
	const int idx = ui->categorySelector->currentIndex();

	return idx >= 0
			? ui->categorySelector->itemData(idx).value<Category*>()
			: 0;
}

Category* MainWindow::addOrFindCategory(const QString &name)
{
	Q_ASSERT(m_database != 0);

	Category* category = m_database->findCategory(name);
	if (category == 0)
	{
		category = m_database->addCategory(name);
	}

	return category;
}

Subject* MainWindow::addOrFindSubject(const QString &name)
{
	Q_ASSERT(m_database != 0);

	Subject* subject = m_database->findSubject(name);
	if (subject == 0)
	{
		subject = m_database->addSubject(name);
	}
	return subject;
}

void MainWindow::categoryChanged(int index)
{
	ui->taskList->clear();

	if (Category* category = currentCategory())
	{
		setTaskList(m_database->tasksByCategory(category));
	}
}

void MainWindow::tasksStarted(Database::TaskList tasks)
{
	QString message = tr("Following tasks has been started:\n");

	foreach(Task* task, tasks)
	{
		message += task->caption() + "\n";
	}

	QMessageBox::information(this, tr("Task(s) started"), message);

	reloadTasks();
}

// Sort predicate by priority and name
bool priorityAndNamePred(Task* left, Task* right)
{
	int leftPriority = left->isStarted() ? left->priority() : -1,
		rightPriority = right->isStarted() ? right->priority() : -1;

	if (leftPriority == rightPriority)
		return QString::compare(left->caption(), right->caption(), Qt::CaseInsensitive) < 0;
	else
		return leftPriority > rightPriority;
}

void MainWindow::setTaskList(Database::TaskList tasks)
{
	qSort(tasks.begin(), tasks.end(), priorityAndNamePred);

	QBrush highPriority(QColor(Qt::red)),
		   lowPriority(QColor(Qt::green)),
		   notStarted(QColor(Qt::gray));

	foreach(Task* task, tasks)
	{
		QListWidgetItem* item = new QListWidgetItem(task->caption());
		item->setData(Qt::UserRole, qVariantFromValue(task));
		if (task->priority() == Task::High)
			item->setForeground(highPriority);
		if (task->priority() == Task::Low)
			item->setForeground(lowPriority);
		if (!task->isStarted())
			item->setForeground(notStarted);
		ui->taskList->addItem(item);
	}

	updateActions();
}

QString MainWindow::databasePath()
{
	return QDir::home().filePath("acidone.db");
}

void MainWindow::newTask()
{
	Task* task = new Task;
	task->addCategory(m_workflow->startCategory());
	task->setSubject(m_meSubject);

	TaskDialog dlg(*task, m_workflow, m_database->subjects(), this);

	if (dlg.exec() == QDialog::Accepted)
	{
		m_database->addTask(task);
		reloadTasks();
	}
	else
	{
		delete task;
	}
}

void MainWindow::databaseChanged()
{
	m_databaseDirty = true;
}

void MainWindow::editCurrentTask()
{
	if (Task* task = currentTask())
	{
		TaskDialog dlg(*task, m_workflow, m_database->subjects(), this);
		if (dlg.exec() == QDialog::Accepted)
		{
			reloadTasks();
		}
	}
}

void MainWindow::doneCurrentTask()
{
	if (Task* task = currentTask())
	{
		Q_ASSERT(m_workflow->canBeDone(task));

		if (QMessageBox::question(this, tr("Done Task"), tr("Mark task as Done?"),
								  QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
		{
			task->complete();
			task->setSingleCategory(m_workflow->endCategory());
			reloadTasks();
		}
	}
}

void MainWindow::removeCurrentTask()
{
	if (Task* task = currentTask())
	{
		if (QMessageBox::warning(this, tr("Delete Task"), tr("Are you sure you want to delete task?"),
								 QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
		{
			m_database->removeTask(task);
			reloadTasks();
		}
	}
}

void MainWindow::newCategory()
{
	QString name = QInputDialog::getText(this, tr("New Category"), tr("Category name"));
	if (!name.isEmpty())
	{
		addOrFindCategory(name);
		reloadCategories();
	}
}

void MainWindow::renameCurrentCategory()
{
	if (Category* category = currentCategory())
	{
		QString name = QInputDialog::getText(this, tr("Rename Category"), tr("Category name"));
		if (!name.isEmpty())
		{
			category->setName(name);
			reloadCategories();
		}
	}
}

void MainWindow::removeCurrentCategory()
{
	if (Category* category = currentCategory())
	{
		if (QMessageBox::warning(this, tr("Delete Category"), tr("Are you sure you want to delete category?"),
								 QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
		{
			m_database->removeCategory(category);
			reloadCategories();
		}
	}
}

void MainWindow::newSubject()
{
	QString name = QInputDialog::getText(this, tr("New Subject"), tr("Subject name"));
	if (!name.isEmpty())
	{
		addOrFindSubject(name);
	}
}

void MainWindow::manageSubjects()
{
	QMessageBox::information(this, tr("Manage subjects"), tr("Not impemented yet"));
}

void MainWindow::showSettings()
{

}

void MainWindow::saveDatabase()
{
	if (m_databaseDirty)
	{
		QFile dbFile(databasePath());
		if (dbFile.open(QFile::WriteOnly))
		{
			acidone::saveDatabase(&dbFile, m_database);
			m_databaseDirty = false;
		}
	}
}

}
